//
// Created by tang on 2022/10/23.
//

#include "wave_app.hpp"

#include "utils/path_utils.hpp"

#include <filesystem>

namespace fs = std::filesystem;

static std::vector<WavePoint> ReadData(const std::string &filename) {
    auto path = fs::path{ Utils::GetDataDirPath() } / filename;
    std::ifstream ifs(path);
    std::vector<WavePoint> result;
    if (ifs.is_open()) {
        std::string line_content;
        while(std::getline(ifs, line_content, '\n'))
        {
            if (line_content.empty() || line_content.front() == ';')
                continue;

            auto colon_pos = line_content.find(':');
            if (colon_pos != std::string::npos)
            {
                float x,y;
                try {
                    x = std::stof(line_content.substr(0, colon_pos));
                    y = std::stof(line_content.substr(colon_pos+1));
                    result.emplace_back(WavePoint{x, y});
                } catch (std::exception &e) {
                    LOG_E("Error: %s", e.what());
                }
            }
        }
    }
    return result;
}

WaveApp::WaveApp(GLFWwindow *window, const char* name) : App(window, name) { }

WaveApp::~WaveApp() { }

void WaveApp::OnInitialize() {
    App::OnInitialize();

    m_points_ = ReadData("waves.txt");

    m_program_ = GL::Program::Create("wave");
    m_vao_ = GL::VertexArray::Create(1);

    // initial vao
    m_vao_->Bind();
    {
        auto buffer = m_vao_->Get(0);
        buffer->Bind(GL_ARRAY_BUFFER);
        buffer->Data(GL_ARRAY_BUFFER, sizeof(WavePoint) * m_points_.size(), m_points_.data(), GL_STATIC_DRAW);
        GLCallValidation(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(WavePoint), (GLvoid*)0));
        GLCallValidation(glEnableVertexAttribArray(0));
    }
    m_vao_->UnBind();
}

void WaveApp::OnCleanup() {
    m_points_.clear();
    App::OnCleanup();
}

void WaveApp::RenderFrame(double delta_time, double total_time) {
    glEnable(GL_BLEND);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glEnable(GL_POINT_SMOOTH);
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_POLYGON_SMOOTH);

    m_program_->Use();

    const glm::vec4 rect{
        0.1f,
        0.9f,
        6.f,
        -6.f
    };

    m_program_->Uniform4f("viewRect", rect);
    glLineWidth(2.f);
    glPointSize(8.f);

    m_vao_->Bind();
    GLCallValidation(glDrawArrays(GL_POINTS, 0, m_points_.size()));
    GLCallValidation(glDrawArrays(GL_LINE_STRIP, 0, m_points_.size()));
    m_vao_->UnBind();

    m_program_->NotUse();
}

DEFINE_FACTORY_FUNC(CreateWaveApp, win, args) {
    (void) args;
    return std::make_unique<WaveApp>(win, "Wave");
}

REGISTER_APP_CREATOR("WaveApp", &CreateWaveApp);
